sign: check signatures for pulled commits
authorDenis Pynkin <denis.pynkin@collabora.com>
Mon, 26 Aug 2019 19:08:10 +0000 (22:08 +0300)
committerDenis Pynkin <denis.pynkin@collabora.com>
Wed, 25 Mar 2020 12:23:54 +0000 (15:23 +0300)
If `verification-key` is set for remote it is used as a public key for
checking the commit pulled from that remote.

Signed-off-by: Denis Pynkin <denis.pynkin@collabora.com>
src/libostree/ostree-repo-pull.c

index 781c2458303507ea6c4b3bf8428eee5b588c4b0a..b16b668d32f43e36ecc4c1d5b39fd6eb7771691c 100644 (file)
@@ -1505,6 +1505,17 @@ ostree_verify_unwritten_commit (OtPullData                 *pull_data,
 
   if (pull_data->sign_verify)
     {
+      /* Nothing to check if detached metadata is absent */
+      if (detached_metadata == NULL)
+        {
+          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "Can't verify commit without detached metadata");
+          return FALSE;
+        }
+      /* Shouldn't happen, but see comment in process_verify_result() */
+      if (g_hash_table_contains (pull_data->verified_commits, checksum))
+        return TRUE;
+
       gboolean ret = FALSE;
       g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit);
       /* list all signature types in detached metadata and check if signed by any? */
@@ -1512,13 +1523,14 @@ ostree_verify_unwritten_commit (OtPullData                 *pull_data,
       for (guint i=0; i < g_strv_length (names); i++)
         {
           g_autoptr (OstreeSign) sign = NULL;
-          g_autoptr(GVariant) signatures = NULL;
+          g_autoptr (GVariant) signatures = NULL;
           g_autofree gchar *signature_key = NULL;
           g_autofree GVariantType *signature_format = NULL;
+          g_autofree gchar *pk_ascii = NULL;
 
           if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL)
           {
-              g_error_free (*error);
+              g_clear_error (error);
               continue;
           }
           signature_key = ostree_sign_metadata_key (sign);
@@ -1528,17 +1540,55 @@ ostree_verify_unwritten_commit (OtPullData                 *pull_data,
                                                signature_key,
                                                signature_format);
 
-          /* Set return to true if any sign fit */
           if (!signatures)
             continue;
 
+          /* TODO: load keys for remote here */
+          ostree_repo_get_remote_option (pull_data->repo,
+                                         pull_data->remote_name,
+                                         "verification-key", NULL,
+                                         &pk_ascii, NULL);
+          if (pk_ascii != NULL)
+            {
+              g_autoptr (GVariant) pk = NULL;
+
+              if (!g_strcmp0(ostree_sign_get_name(sign), "dummy"))
+                {
+                  // Just use the string as signature
+                  pk = g_variant_new_string(pk_ascii);
+                }
+              else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519"))
+                {
+                  gsize key_len = 0;
+                  g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len);
+                  pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar));
+                }
+
+              if (!ostree_sign_set_pk (sign, pk, error))
+                g_clear_error (error);
+            }
+
+          /* Set return to true if any sign fit */
           if (ostree_sign_metadata_verify (sign,
                                            signed_data,
                                            signatures,
                                            error
                                           ))
             ret = TRUE;
+          else
+            g_clear_error (error);
         }
+
+      /* Mark the commit as verified to avoid double verification
+       * see process_verify_result () for rationale */
+      if (ret)
+        {
+        g_hash_table_add (pull_data->verified_commits, g_strdup (checksum));
+        }
+      else
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                             "Can't verify commit");
+
       return ret;
     }
 
@@ -1876,21 +1926,60 @@ scan_commit_object (OtPullData                 *pull_data,
     {
       gboolean ret = FALSE;
       /* list all signature types in detached metadata and check if signed by any? */
-      GStrv names = ostree_sign_list_names();
+      g_auto (GStrv) names = ostree_sign_list_names();
       for (guint i=0; i < g_strv_length (names); i++)
         {
-          g_autoptr (OstreeSign) sign = ostree_sign_get_by_name (names[i], error);
+          g_autoptr (OstreeSign) sign = NULL;
+          g_autofree gchar *pk_ascii = NULL;
 
+          if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL)
+          {
+              g_clear_error (error);
+              continue;
+          }
+          /* TODO: load keys for remote here */
+          ostree_repo_get_remote_option (pull_data->repo,
+                                         pull_data->remote_name,
+                                         "verification-key", NULL,
+                                         &pk_ascii, NULL);
+          if (pk_ascii != NULL)
+            {
+              g_autoptr (GVariant) pk = NULL;
+
+              if (!g_strcmp0(ostree_sign_get_name(sign), "dummy"))
+                {
+                  // Just use the string as signature
+                  pk = g_variant_new_string(pk_ascii);
+                }
+              else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519"))
+                {
+                  gsize key_len = 0;
+                  g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len);
+                  pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar));
+                }
+
+              if (!ostree_sign_set_pk (sign, pk, error))
+                g_clear_error (error);
+            }
+
+
+          /* Set return to true if any sign fit */
           if (ostree_sign_commit_verify (sign,
                                          pull_data->repo,
                                          checksum,
                                          cancellable,
                                          error))
             ret = TRUE;
+          else
+            g_clear_error (error);
+
+        }
+      if (!ret)
+        {
+          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "Can't verify commit");
+          return FALSE;
         }
-      g_strfreev(names);
-      if (ret == FALSE)
-        return FALSE;
     }
 
   /* If we found a legacy transaction flag, assume we have to scan.
@@ -3857,10 +3946,13 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
                                                         &pull_data->gpg_verify_summary, error))
           goto out;
 #endif /* OSTREE_DISABLE_GPGME */
-
-      /* TODO: read option for remote. */
+      /* Fetch verification settings from remote if it wasn't already
+       * explicitly set in the options. */
       if (!opt_sign_verify_set)
-        opt_sign_verify_set = TRUE;
+        if (!ostree_repo_get_remote_boolean_option (self, pull_data->remote_name,
+                                                    "sign-verify", TRUE,
+                                                    &pull_data->sign_verify, error))
+          goto out;
 
       /* NOTE: If changing this, see the matching implementation in
        * ostree-sysroot-upgrader.c